
-----------------------------------------------------------------------
-- MatrixBasic.lua
--
-- A Framework for World Of Warcraft addons
-- Copyright (C) 2007  wwkkii
-- 
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- any later version.
-- 
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
-- 
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
-- 
-- Home Page: http://code.google.com/p/wowmatrix
-----------------------------------------------------------------------

function MatrixUpdateVersion(version)
	local name, ver;

	if (matrix_versions == nil) then
		matrix_versions = { };
	end

	name = version[1];
	ver = version[2] * 100000000 + version[3] * 1000000 + version[4];
	if (matrix_versions[name] and (ver <= matrix_versions[name])) then
		return true;
	end
	matrix_versions[name] = ver;
	return false;
end

local __version__ = { "MatrixBasic", 0, 1, 1 };
if (MatrixUpdateVersion(__version__)) then return; end

matrix = { };
matrix_options = nil;

MATRIX_NEWLN = { };

-----------------------------------------------------------------------
-- Local Functions
-----------------------------------------------------------------------

local function Matrix_CopyTable(src)
	local dst, mt, key, val;

	mt = getmetatable(src);
	if (mt and mt.__nodup__) then
		return src;
	end
	dst = rawget(src, "__copy__");
	if (dst) then
		return dst;
	end
	dst = { }
	rawset(src, "__copy__", dst); 
	for key, val in pairs(src) do
		if (key ~= "__copy__") then
			if (type(val) == "table") then
				dst[key] = Matrix_CopyTable(val);
			else
				dst[key] = val;
			end
		end
	end
	setmetatable(dst, mt);
	return dst;
end

local function Matrix_ResetTable(src)
	local key, val;

	if (rawget(src, "__copy__")) then
		rawset(src, "__copy__", nil);
		for key, val in pairs(src) do
			if (type(val) == "table") then
				Matrix_ResetTable(val);
			end
		end
	end
end

local function Matrix_ShowTable(tbl, nest, limit)
	local prefix, key, val;

	prefix = string.rep("  ", nest);
	for key, val in pairs(tbl) do
		if ((type(val) == "table") and (nest < limit)) then
			matrix.Print(prefix.."["..tostring(key).."] = {");
			Matrix_ShowTable(val, nest + 1, limit);
			matrix.Print(prefix.."}");
		else
			matrix.Print(prefix.."["..tostring(key).."] = "..tostring(val));
		end
	end
end

local function Matrix_InvokeReturn(result, ...)
	if (result) then
		return ...;
	else
		local mesg = ...;
		matrix.__error__ = matrix.GetException(mesg);
		error(mesg, 0);
	end
end

local function Matrix_SafeInvokeReturn(result, ...)
	if (result) then
		return ...;
	else
		local mesg = ...;
		matrix.__error__ = matrix.GetException(mesg);
		return nil;
	end
end

-----------------------------------------------------------------------
-- matrix Basic Functions
-----------------------------------------------------------------------

matrix.Cout = function(r, g, b, ...)
	local mesg, idx, arglist;

	mesg = ""
	arglist = { ... };
	for idx = 1, #arglist, 1 do
		if (arglist[idx] == MATRIX_NEWLN) then
			DEFAULT_CHAT_FRAME:AddMessage(mesg, r, g, b);
			mesg = "";
		else
			mesg = mesg..tostring(arglist[idx]);
		end
	end
	DEFAULT_CHAT_FRAME:AddMessage(mesg, r, g, b);
end

matrix.Chat = function(mode, target, mesg, ...)
	local mesg, idx, arglist;

	mesg = ""
	arglist = { ... };
	for idx = 1, #arglist, 1 do
		if (arglist[idx] == MATRIX_NEWLN) then
			SendChatMessage(mesg, mode, nil, target);
			mesg = "";
		else
			mesg = mesg..tostring(arglist[idx]);
		end
	end
	SendChatMessage(mesg, mode, nil, target);
end

matrix.Print = function(...)
	matrix.Cout(1, 1, 1, ...);
end

matrix.Whisper = function(target, ...)
	matrix.Chat("WHISPER", target, ...);
end

matrix.BattleGround = function(...)
	matrix.Chat("BATTLEGROUND", nil, ...);
end

matrix.Guild = function(...)
	matrix.Chat("GUILD", nil, ...);
end

matrix.Raid = function(...)
	matrix.Chat("RAID", nil, ...);
end

matrix.Party = function(...)
	matrix.Chat("PARTY", nil, ...);
end

matrix.Say = function(...)
	matrix.Chat("SAY", nil, ...);
end

matrix.Yell = function(...)
	matrix.Chat("YELL", nil, ...);
end

matrix.IsName = function(val)
	if ((type(val) == "string") and (val ~= "")) then
		return (not string.find(val, "%s+"));
	end
	return false;
end

matrix.IsKeyName = function(val)
	if ((type(val) == "string") and (val ~= "")) then
		return (string.match(val, "([a-zA-Z_][a-zA-Z0-9_]*)") == val);
	end
	return false;
end

matrix.IsFrame = function(val)
	if (type(value) == "table") then
		return true;
	end
	return false;
end

matrix.CopyValue = function(src)
	if (type(src) == "table") then
		local dst = Matrix_CopyTable(src);
		Matrix_ResetTable(src);
		return dst;
	end
	return src;
end

matrix.ShowValue = function(name, val, nest)
	if (type(val) == "table") then
		if (not nest) then
			nest = 10;
		end
		matrix.Print(tostring(name).." = {");
		Matrix_ShowTable(val, 1, nest);
		matrix.Print("}");
	else
		matrix.Print(tostring(name).." = "..tostring(val));
	end
end

matrix.ToWord = function(src)
	local item, result;

	result = "";
	for item in string.gmatch(src, "%w+") do
		result = result..string.upper(string.sub(item, 1, 1))..string.lower(string.sub(item, 2));
	end
	return result;
end

matrix.GetException = function(mesg)
	local node, file, num, info;

	if (mesg == nil) then
		return matrix.__error__;
	end
	if (type(mesg) == "table") then
		return mesg;
	end
	if (type(mesg) ~= "string") then
		node = { };
		node.message = "Exception with bad info: "..tostring(mesg);
		node.information = mesg;
		return node;
	end

	file, num, info = string.match(mesg, "(.-%.lua):(%d+):(.*)");
	node = matrix.__error__;
	if (not node) then
		node = { };
	end
	if (not file or not num or not info) then
		file = "Unknown.lua";
		num = 0;
		info = mesg;
	end
	node.message = info;
	node.source = file;
	node.number = num;
	return node;
end

matrix.Throw = function(mesg, obj, level)
	local node;

	if (not level) then
		level = 2;
	end
	if (type(mesg) == "table") then
		node = mesg;
		if (not node.message) then
			node.message = "Throw with table info: "..tostring(mesg);
		end
		if (not node.object) then
			node.object = obj;
		end
	elseif (type(mesg) == "string") then
		node = { };
		node.message = mesg;
		node.object = obj;
	else
		node = { };
		node.message = "Throw with bad info: "..tostring(mesg);
		node.information = mesg;
		node.object = obj;
	end
	matrix.__error__ = node;
	error(node.message, level);
end

matrix.Invoke = function(func, ...)
	matrix.__error__ = nil;
	return Matrix_InvokeReturn(pcall(func, ...));
end

matrix.SafeInvoke = function(func, ...)
	matrix.__error__ = nil;
	return Matrix_SafeInvokeReturn(pcall(func, ...));
end
